#pragma once

#include <metalc/cls.h>
#include <dinfra/list.h>
#include <metalc/block/mpsc_list_cnt.h>

#define MSLAB_USE_CLS
/* 试图分离mslab指针和内存指针 */

/* 使用老的flist */
#define OLD_FLIST

typedef struct mslab
{
	/* 要不换一下?第一个cache行用于全局分配，第二个用于本地分配 */
	/* 第一个cache行主要用于本地分配 */
	struct
	{
		dlist_t node;

		/* 线程本地节点，用来维持RCU链表 */
		dlist_t tnode;

		/* bump指针 */
		// void *bump_ptr;
		/* bump_ptr可以和flist指针合一 */
		sword_t consumed_cnt;

		mlistnode_t *flist; // for local free
		

		/* 可以消除吗? */
		hword_t lmax_nr;
		hword_t lcur_nr;	/* 当前消耗的 */

		
	} CACHE_ALIGNED;

	// 64
	/* 第二个cache行用作远程释放 */
	union{
		struct{
			
			hword_t unit_size;
			szcls_t cls;
		};
		word_t cls_size;
	};
	void *allocator; // 拥有者
	mlist_t rlist; // for remote free
} mslab_t;

typedef struct {
	struct mslab CACHE_ALIGNED slab_meta;
} mslab_calign_t;

SAI sword_t mslab_visible_nr(mslab_t *s){
	return atm_ld(&s->rlist.cnt_val);
}
SAI mslab_t *mslab_byptr(void *ptr, const size_t mslab_size)
{
	return (mslab_t *)((paddr_t)ptr & (~(mslab_size - 1UL)));
}
SAI bool_t is_mslab_aligned(void *ptr, const word_t mslab_size)
{
	return !((paddr_t)ptr & (mslab_size - 1UL));
}
SAI void *mslab_ptr_byoffst(void *base, word_t offst)
{
	return (void *)((paddr_t)base + offst);
}

#define MSLAB_OFFST_NEXT ((mlistnode_t *)1)

/* [s][x][u][u][u]...[u]
 * s + x + k * u = SPAN_SIZE;
 * x = SPAN_SIZE - k * u -s 
*/
/* 分离指针和第一个内存 */
SAI void *mslab_init_alloc(mslab_t *s, hword_t unit_size){
	void *ret;
	mlistnode_t *cur;
	// ret = cur_slab->bump_ptr;
	// cur_slab->bump_ptr = (void *)((paddr_t)ret + unit_size);

	/* 新方式 */
	cur = s->flist;
#ifndef OLD_FLIST
	s->flist = cur->next;
	if (s->lcur_nr + 1 < s->lmax_nr)
		s->flist->next = (mlistnode_t *)0x1UL;
	ret = cur;
#else
	s->flist = (void *)((paddr_t)cur + unit_size);
	ret = (void *)((paddr_t)cur - 1);
#endif
	s->consumed_cnt -= 1;
	s->lcur_nr += 1;
	return ret;
}
SAI void mslab_init(mslab_t *s, void *ptr, void *obj_ptr, size_t size, hword_t unit_size, hword_t max_nr, hword_t cls)
{
	// ASSERT(is_ptr_aligned(ptr, size));
	mslab_t *ret = (mslab_t *)s;
	PARAM_USED(size);


	/* 指到第二个cache行位置 */
	/* 如果是对齐的则对齐指针 */

	/* ptr一定是对齐的 */
#ifndef OLD_FLIST
	ret->flist = (mlistnode_t *)ptr;
	// ret->flist->next = (mlistnode_t *)((paddr_t)ptr + 0x1UL + unit_size);
	ret->flist->next = (mlistnode_t *)0x1UL;
	ret->lcur_nr = 1;
#else
	ret->flist = (mlistnode_t *)((paddr_t)ptr + 0x1UL);
	ret->lcur_nr = 0;
#endif
	ret->lmax_nr = max_nr;

	ret->consumed_cnt = 0;


	ret->cls = cls;
	ret->unit_size = unit_size;
	ret->allocator = obj_ptr;
	// ret->ori_allocator = obj_ptr;
	mlist_init(&ret->rlist, (sword_t)max_nr);

}

#define SLAB_FLIST_BLCOKED HC_LOCK_NEXT

SAI mlistnode_t *mslab_try_reclaim(mslab_t *s)
{

	mlistnode_t *ret;

	sword_t tmp_cnt = s->consumed_cnt;
	// ASSERT(s->consumed_cnt + s->lmax_nr >= 0);
	s->consumed_cnt = 0;
	// try reclaim
	s->allocator = NULL_PTR;

	// 回收时提交本地cnt
	ret = mlist_reclaim_all(&s->rlist, tmp_cnt);

	return ret;
}

SAI bool_t __mslab_is_full(mslab_t *s, sword_t lmax_nr){
	return ((s->consumed_cnt + mslab_visible_nr(s)) == lmax_nr);
}
SAI bool_t mslab_is_full(mslab_t *s){
	return ((s->consumed_cnt + mslab_visible_nr(s)) == (sword_t)s->lmax_nr);
}
SAI bool_t mslab_discard(mslab_t *s, void *obj_ptr){
	INFO("[mslab] try reclaim %p before discarded\n", s);
	mlistnode_t *t = mslab_try_reclaim(s);
	if (!t){
		INFO("[mslab]abandoned %p\n", s);
		return TRUE;
	}
	s->flist = t;
	s->allocator = obj_ptr;
	return FALSE;
}
SAI bool_t mslab_discard_ifempty(mslab_t *s, void *obj_ptr)
{
	mlistnode_t *t;
	if (!s->flist)
	{	
		INFO("[mslab] try reclaim %p before discarded\n", s);
		t = mslab_try_reclaim(s);
		if (!t){
			INFO("[mslab]abandoned %p\n", s);
			return TRUE;
		}
		s->flist = t;
		s->allocator = obj_ptr;
	}
	return FALSE;
}

// #define MSLAB_LOCKFREE
// 这个逻辑稍微改进一下
/* TODO: 尝试改成后入队列? */
SAI void *mslab_try_alloc(mslab_t *s, size_t slab_size, hword_t unit_size)
{
	PARAM_USED(slab_size);
	void *ret;
	mlistnode_t *next;
	void *ptr_next;
	// 尝试检查

#ifndef OLD_FLIST
	/* 新flist分配后还会预取下一个，这样对连续分配更有利? */
	mlistnode_t *cur = s->flist;
	ASSERT(cur); /* 一定存在 */
	next = cur->next;
	if (UNLIKELY(next == SLAB_FLIST_BLCOKED)) return SLAB_FLIST_BLCOKED;
	ret = cur;
	s->consumed_cnt -= 1;
	if (next == (mlistnode_t *)0x1UL){
		s->flist = (mlistnode_t *)((paddr_t)ret + unit_size);
		if (++s->lcur_nr == s->lmax_nr) {
			s->flist->next = NULL_PTR;
		}else{
			s->flist->next = (mlistnode_t *)(0x1UL);
		}
	}else{
		s->flist = next;
	}
	return ret;
#else
	/* 优先使用 flist */
	/* case0: flist不为MSLAB_OFFST_NEXT -> 消耗flist */
	/* case1: flist为MSLAB_OFFST_NEXT-> 尝试bump_ptr */
	/* case2: flist不为MSLAB_OFFST_NEXT-> flist被阻塞 -> 直接返回阻塞(此时bump_ptr一定已耗完)*/
	ret = s->flist;
	ASSERT(s->flist != NULL_PTR);
	
	/* 确保不变量flist不为空 */
	/* 改为直接看next, next只可能是1, 空，有节点和阻塞四种结果， */

	/* 能不能消除这个判断 */
	if ((paddr_t)ret & 0x1UL) {
		/* 尝试使用队列 */
		/* ret 可能为空可能不为空 */
		// ASSERT((paddr_t)s->bump_ptr <= (paddr_t)s + slab_size - unit_size);
		/* 不需要判断 */
		ASSERT(s->lcur_nr < s->lmax_nr);
		/* 最后一位依然为1 */
		s->flist = (mlistnode_t *)((paddr_t)ret + unit_size);
		s->consumed_cnt -= 1;

		if (++s->lcur_nr == s->lmax_nr) {
			// INFO("[mslab] slab all footprint, cur bump_ptr %p, max_nr %u, running out\n", s->bump_ptr, s->lmax_nr);
			s->flist = NULL_PTR;
		}else{
			s->flist = (mlistnode_t *)((paddr_t)ret + unit_size);
		}

		ret = (void *)((paddr_t)ret - 1); /* 关闭最后一位 */
		ASSERT(s->consumed_cnt + s->lmax_nr >= 0);
		// ASSERT(((paddr_t)ret >= ((paddr_t)mslab_byptr(ret, slab_size))) && ((paddr_t)ret < ((paddr_t)mslab_byptr(ret, slab_size) + slab_size)) && ((paddr_t)ret + unit_size) <= ((paddr_t)mslab_byptr(ret, slab_size) + slab_size));

		return ret;
	}
	ASSERT(SLAB_FLIST_BLCOKED != MSLAB_OFFST_NEXT);
	next = mlistnode_consume_head(s->flist);
	if (LIKELY(next != SLAB_FLIST_BLCOKED)){
		/* 消费成功，直接返回 */
		INFO("[mslab] consumed in flist %p, next %p\n", ret, next);
		s->flist = next;
		s->consumed_cnt -= 1;
		// ASSERT(((paddr_t)ret >= ((paddr_t)mslab_byptr(ret, slab_size))) && ((paddr_t)ret < ((paddr_t)mslab_byptr(ret, slab_size) + slab_size)) && ((paddr_t)ret + unit_size) <= ((paddr_t)mslab_byptr(ret, slab_size) + slab_size));
		// ASSERT(((paddr_t)ret < ((paddr_t)s + slab_size)) && ((paddr_t)ret + unit_size) <= ((paddr_t)s + slab_size));
		return ret;
	}
	/* 消费失败，尝试直接开辟 */

	/* 消费失败的情况 下面一定已经被消耗完?*/
	return SLAB_FLIST_BLCOKED;
#endif
}

SAI sword_t mslab_local_free(mslab_t *s, mlistnode_t *hd, mlistnode_t *tl, sword_t nr)
{
	sword_t ret;
	tl->next = s->flist;
	s->flist = hd;
	ret = s->consumed_cnt + nr;
	s->consumed_cnt = ret;
	return ret;
}

SAI sword_t mslab_remote_free(mslab_t *s, mlistnode_t *hd, mlistnode_t *tl, sword_t nr)
{
	sword_t cnt = mlist_push_batch(&s->rlist, hd, tl, nr);
	return cnt;
}


